package com.base.animation.helper

import android.graphics.PointF
import android.util.Log
import com.base.animation.cache.DisplayItemCache
import com.base.animation.item.BaseDisplayItem
import com.base.animation.model.AnimDrawObject
import com.base.animation.model.AnimPathObject
import com.base.animation.model.DrawObject
import com.base.animation.model.toAnimDrawObject
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope.coroutineContext
import kotlinx.coroutines.ObsoleteCoroutinesApi
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.newFixedThreadPoolContext
import kotlinx.coroutines.supervisorScope
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.math.ceil

/**
 * @author:zhouzechao
 * @date: 2020/12/9
 * description：处理animPathObject转每一帧的绘制点
 */
private const val TAG = "PathObjectDeal"

@ObsoleteCoroutinesApi
class PathObjectDeal @JvmOverloads constructor() {

    var intervalDeal: Long = 0L

    private val animDisplayScope = CoroutineScope(newFixedThreadPoolContext(4, "calculation"))

    private val displayItemCache = DisplayItemCache()

    val animDrawObjects: CopyOnWriteArrayList<DrawObject> = CopyOnWriteArrayList()

    private var hasCache = false

    @Volatile
    var lastCacheTime = 0L
    var cacheTime = 60 * 1000L
        set(value) {
            field = if (value < 60) {
                60 * 1000L
            } else {
                value * 1000L
            }
        }

    private var loggingExceptionHandler = CoroutineExceptionHandler { context, throwable ->
        Log.e("CoroutineException", "Coroutine exception occurred. $context", throwable)
    }

    /**
     * 路径ID和绘制ID对应map
     */
    //val displayPathIdWithDrawId = mutableMapOf<String, BaseDisplayItem>()

    private val animPather = animDisplayScope.actor<AnimPathObject>(coroutineContext +
        loggingExceptionHandler,
        capacity = 300) {
        supervisorScope {
            for (animPath in this@actor) {
                launch(coroutineContext) {
                    Log.i(TAG, "for ---- ${Thread.currentThread().name}-------start")
                    displayItemCache.putDisplayItems(animPath.displayItemsMap)
                    val drawsMap = mutableMapOf<Int, MutableList<AnimDrawObject>>()
                    var position = 0
                    val drawObject = DrawObject()
                    animPath.startPoints.forEachIndexed { index, start ->
                        drawsMap[position] = mutableListOf(start.toAnimDrawObject())
                        animPath.animPathMap[index]?.apply {
                            val duringTime = during
                            val times = ceil(duringTime / intervalDeal.toDouble()).toInt()
                            Log.i(TAG, "times:$times pathObjects.size:${pathObjects.size}")
                            pathObjects.forEach {
                                Log.i(TAG, "pathObject:${it.point}")
                                it.getItem(start)
                            }
                            for (i in 0..times) {
                                position++
                                var p = i * intervalDeal.toFloat() / duringTime
                                if (p > 1f) {
                                    p = 1f
                                }
                                val interP = start.interpolator.getInterpolation(p)
                                Log.i(TAG, "i:$i p:$p position:$position interP:$interP")
                                val animDrawObjects = mutableListOf<AnimDrawObject>()
                                pathObjects.forEach {
                                    animDrawObjects.add(AnimDrawObject(start.displayItemId).apply {
                                        point =
                                            PointF(start.point.x + it.itemX * interP,
                                                start.point.y + it.itemY * interP)
                                        alpha = start.alpha + (it.itemAlpha * interP).toInt()
                                        scaleX = start.scaleX + it.itemScaleX * interP
                                        scaleY = start.scaleY + it.itemScaleY * interP
                                        rotation = start.rotation + it.itemRotation * interP
                                    })
                                }
                                Log.i(TAG, "position:$position animDrawObjects:$animDrawObjects")
                                drawsMap[position] = animDrawObjects
                            }
                        }
                    }
                    drawObject.animDraws = drawsMap
                    animDrawObjects.add(drawObject)
                    Log.i(TAG, "for ---- ${Thread.currentThread().name}-------end")
                    delay(50L)
                }
            }
        }
    }

    fun getDisplayItem(displayItemId: String): BaseDisplayItem? {
        return displayItemCache.getDisplayItem(displayItemId)
    }

    fun recycler(displayItem: BaseDisplayItem) {
        displayItemCache.recycler(displayItem)
    }

    fun sendAnimPath(animPathObject: AnimPathObject) {
        hasCache = true
        lastCacheTime = cacheTime
        animPather.offer(animPathObject)
    }

    fun release() {
        animDrawObjects.clear()
        displayItemCache.clear()
        animPather.close()
    }

    fun runCacheStrategy() {
        if (hasCache) {
            lastCacheTime -= intervalDeal
            if (lastCacheTime <= 0) {
                displayItemCache.clear()
                hasCache = false
            }
        }
    }
}


